ASP net y Razor pages

Descripcion

Como crear páginas ASP con una plantilla vacia y crear páginas con Razor Pages.

Plantilla ASP .Net Core vacía

Primero crearemos una plantilla vacía de ASP .Net Core y a partir de ahí iremos añadiendo cosas.

Creamos un nuevo proyecto y seleccionamos la plantilla ASP .Net Core Empty:

Le damos un nombre al proyecto:

Dejamos las opciones por defecto:

El proyecto se crea con la siguiente estructura de archivos:

En el archivo Program.cs tenemos el siguiente contenido:

Al ejecutar el proyecto se ejecutará el servidor web Kestrel y se abrirá un navegador para conectarse al servidor, como podemos ver se devuelve el contenido que hemos indicado en el Endpoint.

Crear un Startup para separar la configuración del proyecto

Podemos usar directamente el archivo Program.cs para inicializar nuestro servicio, pero es mas eficiente separar la configuración en un archivo Startup.cs.

Dentro de nuestro proyecto creamos un archivo de clase llamado Startup.cs:

Dentro de la clase Startup crearemos dos metodos: ConfigureServices y Configure, el código sería tal que así:

namespace WebAppExample
{
  public class Startup
  {
      public void ConfigureServices(IServiceCollection services)
      {

      }

      public void Configure( IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseHsts();
          }
          app.UseRouting();
          app.UseHttpsRedirection();
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapGet("/", () => "Hello World");
          });
      }
  }
}

Modificamos la clase Program.cs para que quede tal que así:

using WebAppExample;

Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
    webBuilder.UseStartup();
}).Build().Run();
Servir contenido estático

Actualmente solo tenemos un endpoint que devuelve la cadena "Hello World", vamos a cambiar la configuración de nuestro proyecto para que sirva páginas con contenido estático.

Primero creamos un directorio wwwroot dentro de nuestro proyecto, dentro de este directorio introduciremos las páginas que queramos que sirva el servidor:

Dentro de wwwroot podemos introducir un index.html simple para probar que funciona:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h1>Página HTML estática</h1>
</body>
</html>

Añadimos las siguientes líneas en nuestro Startup.cs:

app.UseDefaultFiles(); //Permite asociar archivos por defecto (como index.html o default.html) al endpoint "/"
app.UseStaticFiles(); //Permite servir archivos estáticos

El Startup.cs completo se queda así:

namespace WebAppExample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {

        }

        public void Configure( IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseHsts();
            }
            app.UseRouting();
            app.UseHttpsRedirection();

            app.UseDefaultFiles(); //Permite asociar archivos por defecto (como index.html o default.html) al endpoint "/"
            app.UseStaticFiles(); //Permite servir archivos estáticos

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/hola", () => "Hello World");
            });
        }
    }
}

Hemos cambiado el endpoint "/" por "/hola" para que no entre en conflicto a la hora de servir las páginas estáticas.

Al ejecutar el proyecto vemos que se devuelve nuestro index.html:

Y al entrar en el endpoint "/hola" se devuelve el string que teníamos configurado:

Razor Pages

Las Razor Pages nos permiten introducir código C# en las páginas html de manera que nos permiten generar páginas de manera dinámica.

Para utilizar las Razor Pages primero tenemos que crear una carpeta Pages que será donde vayan colocados nuestros archivos .cshtml:

Movemos nuestro archivo index.html a la carpeta Pages y le cambiamos la extension a .cshtml:

Modificamos el código de archivo index.cshtml para dejarlo tal que así:

@page
@functions
{
    public int Epoch { get; set; }

    public void OnGet()
    {
        Model.Epoch = (int) (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds;
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h1>Unix time: @Model.Epoch</h1>
</body>
</html>

Todas las sentencias que empiezan por @ indican una sentencia de Razor la sentencía @page indica que es una Razor page y que contendrá contenido dinamico, en el bloque @functions definimos nuestro código y despues en medio del código html usamos @Model para llamar a la variable que hemos declarado.

Añadimos la siguiente configuracion en el archivo Startup.cs para activar las Razor Pages:

En el metodo ConfigureServices:

services.AddRazorPages();

En la lambda de UseEndpoints:

endpoints.MapRazorPages();

El código de Startup.cs queda así:

namespace WebAppExample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
        }

        public void Configure( IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseHsts();
            }
            app.UseRouting();
            app.UseHttpsRedirection();

            app.UseDefaultFiles(); //Permite asociar archivos por defecto (como index.html o default.html) al endpoint "/"
            app.UseStaticFiles(); //Permite servir archivos estáticos

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapGet("/hola", () => "Hello World");
            });
        }
    }
}

Una vez hechos estos cambios ejecutamos el proyecto y veremos que cada vez que ejecutamos la página el valor de Epoch cambia:

Usar Layouts

El uso de layouts nos permite reutilizar código a lo largo de varias páginas, por ejemplo el código html de un menu en una web.

Primero creamos el archivo _ViewStart.cshtml dentro de la carpeta pages, podemos hacerlo directamente con una plantilla de Visual Studio

click derecho en Pages > Add > New Item

Seleccionamos la plantilla Razor View Start

Se nos crea directamente el archivo _ViewStart.cshtml con el contenido necesario:

Creamos una carpeta Shared dentro de la carpeta Pages, dentro de esta carpeta shared crearemos un archivo _layout.cshtml que será el que contenga el código compartido entre las páginas, en este caso usaremos un mini menú:

click derecho en Shared > Add > New Item

Y seleccionamos Razor Layout:

La estructura de archivos queda tal que así:

En el archivo _Layout.cshtml dejamos el código tal que así:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div><a href="/">Epoch</a> | <a href="/dia">Dia</a>  </div>
    <div>
        @RenderBody()
    </div>
</body>
</html>

La funcion @RenderBody() será la que renderice el código de la página en la que nos encontramos.

A continuación tenemos que modificar la página index.cshtml, para eliminar todo el código que ya está puesto en el archivo _Layout.cshtml y dejar solo lo relativo a dicha página, el contenido de index.cshtml quedaría tal que así:

@page
@functions
{
    public int Epoch { get; set; }

    public void OnGet()
    {
        Model.Epoch = (int) (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds;
    }
}

<h1>Unix time: @Model.Epoch</h1>

A continuación añadimos una segunda página en la que mostraremos el día actual de la semana. Para crear la segunda página podemos duplicar el index.cshtml, pero esto generará un problema, la manera correcta en teoría es utilizando la plantilla Razor Page - Empty, pero en este caso como será una página muy simple solo duplicaremos el archivo index.cshtml y lo renombramos como Dia.cshtml, mas abajo se explica el problema que surge y como solucionarlo.

El código de la nueva página solo sería el encabezado que muestra el día de la semana, el resto del código ya se carga directamente del layout. El código sería el siguiente:

@page
@functions
{
    public String Dia { get; set; }

    public void OnGet()
    {
        Model.Dia = DateTime.Now.DayOfWeek.ToString();
    }
}

<h1>Dia de la semana: @Model.Dia</h1>

Solucionar problema de objeto eliminado en el proyecto

Al duplicar una página o añadirla como una clase mas (sin usar la plantilla de Razor Page), el Visual Studio bloquea dicha página para que no se pueda acceder en el archivo del proyecto (en el .csproj):

Como se puede ver en la configuración el archivo Dia.cshtml aparece como "Remove", no solo tenemos que asegurarnos de ELIMINAR esa linea, si no que tambien tenemos que asegurarnos que aparezca como Objeto añadido (en este caso como "None"), otro fallo que se puede dar es que salga añadido pero como Compile en vez de None, en ese caso solo tendríamos que cambiar Compile a None y listo.

En este caso el archivo del proyecto quedaría asi:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>

  <ItemGroup>
    <None Include="Pages\Dia.cshtml" />
    <None Include="Pages\index.cshtml" />
  </ItemGroup>

</Project>

Ahora tenemos el menu arriba para seleccionar la página, y al seleccionar día se carga la nueva página:

Tags

C# | ASP | Razor Pages